#include "sal.h"
#include "ctc_cli.h"
#include "api/include/ctcs_api.h"
#include "dal.h"
#include "ctc_shell_server.h"
//#include <sys/mman.h>

extern int32 ctc_phy_cli_init(uint8 cli_tree_mode);
extern int32 ctc_app_cli_init(uint8 cli_tree_mode);
extern int ctc_asw_cli_master(uint8 ctc_shell_mode);
extern int32 ctc_asw_chip_special_cli_init();

int32 g_error_on = 0;
uint8 g_api_lchip = 0;
uint8 g_lchip_num = 1;
ctc_debug_out_func g_ctc_debug_show_print_cb = sal_printf;

#ifdef CTC_CLI_EN
#define CTC_CLI_OUT ctc_cli_out_basic
#else
#define CTC_CLI_OUT sal_printf
#endif

#ifdef CTC_CLI_EN
uint8 cli_end = 0;
void
ctc_cli_mode_exit(ctc_vti_t* vty)
{
    switch (vty->node)
    {
    case EXEC_MODE:
        vty->quit(g_ctc_vti);
        break;

    case CTC_ASW_MODE:
        vty->node = EXEC_MODE;
        break;

    default:
        vty->node = EXEC_MODE;
        break;

        break;
    }
}

CTC_CLI(exit_config,
        exit_cmd,
        "exit",
        "End current mode and down to previous mode")
{
    ctc_cli_mode_exit(vty);
    return CLI_SUCCESS;
}

CTC_CLI(quit_config,
        quit_cmd,
        "quit",
        "Exit current mode and down to previous mode")
{
    ctc_cli_mode_exit(vty);
    return CLI_SUCCESS;
}

CTC_CLI(ctc_cli_common_fast_enter_sdk_mode,
        ctc_cli_common_fast_enter_sdk_mode_cmd,
        "asw",
        "Asw mode")
{
    g_ctc_vti->node  = CTC_ASW_MODE;

    return CLI_SUCCESS;
}


ctc_cmd_node_t asw_node =
{
    CTC_ASW_MODE,
    "\rCTC_CLI(ctc-asw)# ",
};

ctc_cmd_node_t exec_node =
{
    EXEC_MODE,
    "\rCTC_CLI# ",
};

#if defined(SDK_IN_USERMODE)
CTC_CLI(ctc_cli_common_exit_server,
        ctc_cli_common_exit_server_cmd,
        "exit server",
        "Exit current mode and down to previous mode",
        "Exit Server Progress")
{
    ctcs_interrupt_deinit(0);

    exit(0);

    return CLI_SUCCESS;
}
#endif

int ctc_master_printf(struct ctc_vti_struct_s* vti, const char *szPtr, const int szPtr_len)
{
    sal_write(0,(void*)szPtr,szPtr_len);
    return 0;
}

int ctc_master_quit(struct ctc_vti_struct_s* vti)
{
    uint8 lchip_idx = 0;

    for(lchip_idx=0;lchip_idx<CTC_MAX_LOCAL_CHIP_NUM;lchip_idx++)
    {
        ctcs_interrupt_deinit(lchip_idx);
    }    cli_end = 1;

    return 0;
}

int
ctc_asw_cli_master(uint8 ctc_shell_mode)
{
    uint32  nbytes = 0;
    int8*   pread_buf = NULL;

    ctc_vti_init_client(&g_vty_client);
    ctc_cmd_init(0);

    ctc_install_node(&asw_node, NULL);
    ctc_install_node(&exec_node, NULL);
    ctc_vti_init(CTC_ASW_MODE);

    install_element(EXEC_MODE, &ctc_cli_common_fast_enter_sdk_mode_cmd);
    install_element(CTC_ASW_MODE, &exit_cmd);
    install_element(CTC_ASW_MODE, &quit_cmd);
    install_element(EXEC_MODE, &exit_cmd);
    install_element(EXEC_MODE, &quit_cmd);

    ctc_asw_chip_special_cli_init();
#ifdef CTC_PHY_EN
    ctc_phy_cli_init(CTC_ASW_MODE);
#endif
    ctc_sort_node();
    g_vty_client.ctc_vti->printf = ctc_master_printf;
    g_vty_client.ctc_vti->quit   = ctc_master_quit;
    if (ctc_shell_mode == 1)
    {
#if defined(SDK_IN_USERMODE)
        install_element(EXEC_MODE, &ctc_cli_common_exit_server_cmd);
        install_element(CTC_ASW_MODE, &ctc_cli_common_exit_server_cmd);
#endif
        return ctc_vty_socket();
    }
    else
    {
        pread_buf = sal_malloc(CTC_VTI_BUFSIZ);

        if (NULL == pread_buf)
        {
            return -1;
        }
        set_terminal_raw_mode(CTC_VTI_SHELL_MODE_DEFAULT);
        g_vty_client.ctc_vti->printf = ctc_master_printf;
        g_vty_client.ctc_vti->quit   = ctc_master_quit;
        while (cli_end == 0)
        {
            nbytes = ctc_vti_read(pread_buf, CTC_VTI_BUFSIZ, CTC_VTI_SHELL_MODE_DEFAULT);
            ctc_vti_read_cmd(g_vty_client.ctc_vti, pread_buf, nbytes);
        }
        restore_terminal_mode(CTC_VTI_SHELL_MODE_DEFAULT);
        sal_free(pread_buf);
        pread_buf = NULL;
    }
   
    return 0;
}
#endif

int32
ctc_app_isr_sw_learning_process_isr(uint8 gchip, void* p_data)
{
    ctc_learning_cache_t* p_cache = (ctc_learning_cache_t*)p_data;
    ctc_l2_addr_t l2_addr;
    uint32 index = 0;
    int32 ret = CTC_E_NONE;
    uint8 lchip = 0;
    sal_memset(&l2_addr, 0, sizeof(l2_addr));
    for (index = 0; index < p_cache->entry_num; index++)
    {
        // for mac limit
        if (CTC_FLAG_ISSET(p_cache->learning_entry[index].flag,CTC_L2_LEARN_CACHE_OVER_MAC_LIMIT))
        {
            CTC_CLI_OUT("%% new sa is over mac limit\n");
            return ret;
        }
        // for station move
        if (CTC_FLAG_ISSET(p_cache->learning_entry[index].flag,CTC_L2_LEARN_CACHE_STATION_MOVE))
        {
            CTC_CLI_OUT("%% station move fifo to cpu\n");
            return ret;
        }
        l2_addr.fid = p_cache->learning_entry[index].fid;
        l2_addr.flag = 0;
        l2_addr.mac[0] = p_cache->learning_entry[index].mac[0];
        l2_addr.mac[1] = p_cache->learning_entry[index].mac[1];
        l2_addr.mac[2] = p_cache->learning_entry[index].mac[2];
        l2_addr.mac[3] = p_cache->learning_entry[index].mac[3];
        l2_addr.mac[4] = p_cache->learning_entry[index].mac[4];
        l2_addr.mac[5] = p_cache->learning_entry[index].mac[5];
        l2_addr.gport = p_cache->learning_entry[index].global_src_port;
        ret = ctcs_l2_add_fdb(lchip, &l2_addr);

        if (ret)
        {
            CTC_CLI_OUT("%%Learning fdb fail ret = %d\n", ret);
        }
    }

    return ret;
}

int32
ctc_app_isr_port_link_isr(uint8 gchip, void* p_data)
{
    ctc_port_link_status_t* p_link = NULL;
    uint16 gport = 0;
    uint32 value = FALSE;
    int32 ret = CTC_E_NONE;

    p_link = (ctc_port_link_status_t*)p_data;
    gport = p_link->gport;

    ret = ctcs_port_get_property(0, gport, CTC_PORT_PROP_LINK_UP, &value);
    if (value)
    {
        CTC_CLI_OUT("gport 0x%04X Link Up, Port is enabled! \n", gport);
    }
    else
    {
        CTC_CLI_OUT("gport 0x%04X Link Down, Port is disabled, please do port enable when linkup again! \n", gport);
    }

    /*Step1: Disable Port*/

    /*Do other thing for link down*/

    return ret;
}

int32
ctc_app_isr_init(uint8 lchip)
{
    /* 1. register event callback */
    ctcs_interrupt_register_event_cb(lchip, CTC_EVENT_L2_SW_LEARNING, ctc_app_isr_sw_learning_process_isr);
    ctcs_interrupt_register_event_cb(lchip, CTC_EVENT_PORT_LINK_CHANGE,  ctc_app_isr_port_link_isr);
    #if 0
    ctcs_interrupt_register_event_cb(lchip, CTC_EVENT_ECC_ERROR, ctcs_app_isr_ecc_error_isr);
    #endif

    return CTC_E_NONE;
}

/**
 @brief The sample code for initialize SDK

 @param[in] ctc_shell_mode  mode 0, get commands from screen.
                            mode 1, C/S mode, client and server communicate by socket/netlink. The shell server start 
up with SDK, and the client is ctc_shell.
                            mode 2, user define mode, input commands by ctc_vti_cmd_input() and output result to user 
by ctc_vti_cmd_output_register().

 @param[in] ctcs_api_en   ctc api mode or ctcs api mode

 @remark  Refer to SDK PG and APP for detail description

 @return CTC_E_XXX

*/

int32
userinit(uint8 ctc_shell_mode, uint8 ctcs_api_en, void* p_init_config)
{
    uint8 lchip = 0;
    int32 ret = CTC_E_NONE;
    ctc_acl_global_cfg_t acl_global_cfg;
    dal_op_t dal_cfg;
    static ctc_datapath_global_cfg_t datapath_cfg = {.core_frequency_a = 125,
    .serdes = {
        {0, CTC_CHIP_SERDES_SGMII_MODE, 0, 0, 0}, {0, CTC_CHIP_SERDES_SGMII_MODE, 1, 0, 0}, {0, CTC_CHIP_SERDES_SGMII_MODE, 0, 0, 0}, {0, CTC_CHIP_SERDES_SGMII_MODE, 0, 0, 0},/*serdes 0~3*/
        {0, CTC_CHIP_SERDES_QSGMII_MODE, 0, 0, 0}, {0, CTC_CHIP_SERDES_QSGMII_MODE, 0, 0, 0}, {0, CTC_CHIP_SERDES_QSGMII_MODE, 0, 0, 0}, {0, CTC_CHIP_SERDES_QSGMII_MODE, 0, 0, 0},/*serdes 4~7*/
        {0, CTC_CHIP_SERDES_PHY_MODE, 0, 0, 0}, {0, CTC_CHIP_SERDES_PHY_MODE, 0, 1, 0}, {0, CTC_CHIP_SERDES_PHY_MODE, 0, 0, 0}, {0, CTC_CHIP_SERDES_PHY_MODE, 0, 0, 0},/*serdes 8~11*/
        {0, CTC_CHIP_SERDES_PHY_MODE, 0, 0, 0}, {0, CTC_CHIP_SERDES_PHY_MODE, 0, 0, 0}, {0, CTC_CHIP_SERDES_PHY_MODE, 0, 0, 0}, {0, CTC_CHIP_SERDES_PHY_MODE, 0, 0, 0},/*serdes 12~15*/
    }
    };
    ctc_chip_device_info_t chip_device_info;
    ctc_l2_fdb_global_cfg_t l2_fdb_global_cfg;
    ctc_security_global_cfg_t security_global_cfg;
    ctc_intr_global_cfg_t intr_glb_cfg;
    ctc_intr_group_t intr_grp;
    ctc_chip_global_cfg_t chip_cfg;
    static ctc_chip_phy_mapping_para_t phy_mapping = {
        {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xff},
        {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xff}
    };
    sal_systime_t tv1,tv2;
    static ctc_qos_global_cfg_t ctc_qos_global_cfg;
#ifdef CTC_CLI_EN
    mem_mgr_init();
#endif
    sal_memcpy(&dal_cfg, &g_dal_op, sizeof(dal_op_t));
    dal_cfg.lchip = lchip;
    dal_op_init(&dal_cfg);
    sal_memset(&security_global_cfg, 0, sizeof(ctc_security_global_cfg_t));
    sal_memset(&intr_glb_cfg, 0, sizeof(ctc_intr_global_cfg_t));
    sal_memset(&intr_grp, 0, sizeof(intr_grp));
    for (lchip = 0; lchip < g_lchip_num; lchip ++ )
    {
        sal_gettime(&tv1);
        ctcs_install_api(lchip);
        ctcs_chip_init(lchip, 1);
        
        ret = ctcs_chip_get_property(lchip, CTC_CHIP_PROP_DEVICE_INFO, (void*)&chip_device_info);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to get device info %d\n", ret);
            return ret;
        }

        CTC_CLI_OUT("Lchip %d Chip name: %s, Device Id: %d, Version Id: %d \n", lchip, chip_device_info.chip_name, chip_device_info.device_id, chip_device_info.version_id);

        chip_cfg.cpu_port = 28;
        chip_cfg.cpu_port_en = 1;
        chip_cfg.tpid = 0x9100;
        chip_cfg.cpu_mac_da[0][0] = 0xFE;
        chip_cfg.cpu_mac_da[0][1] = 0xFD;
        chip_cfg.cpu_mac_da[0][2] = 0x00;
        chip_cfg.cpu_mac_da[0][3] = 0x00;
        chip_cfg.cpu_mac_da[0][4] = 0x00;
        chip_cfg.cpu_mac_da[0][5] = 0x00;
        chip_cfg.io_wr_dis = 0;
        ret = ctcs_set_chip_global_cfg(lchip, &chip_cfg);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to set cpu port\n");
            return ret;
        }
        ctcs_datapath_init(lchip, &datapath_cfg);
        
        ret = ctcs_register_init(lchip, NULL);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init register module\n");
            return ret;
        }
        intr_glb_cfg.group_count = 1;
        intr_grp.group = 0;
        intr_grp.irq = 44;
        intr_grp.prio = SAL_TASK_PRIO_DEF;
        sal_memcpy(intr_grp.desc, "interrupt group0", sizeof("interrupt group0"));
        intr_glb_cfg.p_group = &intr_grp;
        intr_glb_cfg.intr_mode = 0;
        ret = ctcs_interrupt_init(lchip, &intr_glb_cfg);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init interrupt module\n");
            return ret;
        }
        ret = ctcs_port_init(lchip, NULL);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init port module\n");
            return ret;
        }
        ret =  ctcs_pdu_init(lchip, NULL);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init pdu module\n");
            return ret;
        }
        ret = ctcs_vlan_init(lchip, NULL);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init vlan module\n");
            return ret;
        }
        l2_fdb_global_cfg.hw_learn_en = 1;
        ret = ctcs_l2_fdb_init(lchip, &l2_fdb_global_cfg);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init fdb module\n");
            return ret;
        }
        
        ret = ctcs_learning_aging_init(lchip, NULL);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init learing aging module\n");
            return ret;
        }
        ctc_qos_global_cfg.resrc_pool.igs_pool_mode = CTC_QOS_RESRC_POOL_DISABLE;
        ret = ctcs_qos_init(lchip, &ctc_qos_global_cfg);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init qos module\n");
            return ret;
        }
        ret = ctcs_security_init(lchip, &security_global_cfg);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init security module\n");
            return ret;
        }
        ret = ctcs_acl_init(lchip, &acl_global_cfg);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init acl module\n");
            return ret;
        }
        ret = ctcs_mirror_init(lchip, NULL);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init mirror module\n");
            return ret;
        }
        ret = ctc_app_isr_init(lchip);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to init app isr\n");
            return ret;
        }
        ret =  ctcs_chip_set_property(lchip, CTC_CHIP_PROP_PHY_MAPPING, &phy_mapping);
        if (ret < 0)
        {
            CTC_CLI_OUT("Error to set phy mapping\n");
            return ret;
        }
        sal_gettime(&tv2);
        if(tv2.tv_usec >= tv1.tv_usec)
        {
           CTC_CLI_OUT("USED  TIME:[%10u s:%3u ms]\n", tv2.tv_sec - tv1.tv_sec, (tv2.tv_usec - tv1.tv_usec)/1000);
        }
        else
        {
           CTC_CLI_OUT("USED  TIME:[%10u s:%3u ms]\n", tv2.tv_sec - tv1.tv_sec - 1, (tv2.tv_usec + 1000000 - tv1.tv_usec)
        /1000);
        }

#ifdef CTC_PHY_EN
        extern int32 phy_init(uint8 load_mode, const char *pathname);
        phy_init(0, NULL);
#endif
    }

#ifdef CTC_CLI_EN
    ctc_asw_cli_master(ctc_shell_mode);
#endif

    return 0;
}
CTC_EXPORT_SYMBOL(userinit);
#if defined(SDK_IN_USERMODE)
int
main(const int argc, char* argv[])
{
    int32 ret = 0;
    uint8 loop = 0;
    uint8 ctc_shell_mode = 1;

    while(++loop < argc)
    {
        if (!sal_strcmp(argv[loop], "cli"))
        {
            ctc_shell_mode =  0;
        }
    }
    ret = userinit(ctc_shell_mode, 1, NULL);
    return ret;
}
#endif
